﻿using Abp.Domain.Repositories;
using System;
using Abp.Application.Services.Dto;
using FutureApp.Shop.Dto;
using System.Threading.Tasks;
using Abp.AutoMapper;
using System.Collections.Generic;
using System.Linq;
using Abp.Linq.Extensions;
using Abp.Extensions;
using Abp.UI;

namespace FutureApp.Shop
{
    ///<AppService/>
    public class GoodsAppService : FutureAppAppServiceBase, IGoodsAppService
    {
        private IRepository<Goods, Guid> GoodsRepository { get; }
        private IRepository<GoodsItem, Guid> GoodsItemRepository { get; }
        private IRepository<GoodsCategory, Guid> GoodsCategoryRepository { get; }
        private IRepository<GoodsBrand, Guid> GoodsBrandRepository { get; }
        private IRepository<GoodsMould, Guid> GoodsMouldRepository { get; }

        /// <constructor/>
        public GoodsAppService(
            IRepository<Goods, Guid> goodsRepository,
            IRepository<GoodsItem, Guid> goodsItemRepository,
            IRepository<GoodsBrand, Guid> goodsBrandRepository,
            IRepository<GoodsCategory, Guid> goodsCategoryRepository
        )
        {
            this.GoodsRepository = goodsRepository;
            this.GoodsItemRepository = goodsItemRepository;
            this.GoodsCategoryRepository = goodsCategoryRepository;
            this.GoodsBrandRepository = goodsBrandRepository;
        }

        /// <inheritDoc/>
        public PagedResultDto<GoodsItemEntityDto> GetGoodsList(GetGoodsListInput input)
        {
            var queryable = this.GoodsItemRepository.GetAll()
                .WhereIf(!input.SerialNo.IsNullOrWhiteSpace(), e => e.SerialNo == input.SerialNo)
                .WhereIf(!input.Name.IsNullOrWhiteSpace(), e => e.Name.Contains(input.Name))
                .WhereIf(!input.BrandName.IsNullOrWhiteSpace(), e => e.Brand.Name.Contains(input.BrandName))
                .WhereIf(!input.CategoryName.IsNullOrWhiteSpace(), e => e.Category.Name.Contains(input.CategoryName))
                .WhereIf(!input.MouldName.IsNullOrWhiteSpace(), e => e.Mould.Name.Contains(input.MouldName));
            var count = queryable.Count();
            var list = queryable.OrderByDescending(e => e.LastModificationTime ?? e.CreationTime).PageBy(input).ToList();
            return new PagedResultDto<GoodsItemEntityDto>(count, list.MapTo<List<GoodsItemEntityDto>>());
        }

        /// <inheritDoc/>
        public async Task<GoodsItemEntityDto> GetGoodsById(Guid id)
        {
            if (!await this.ExistGoods(id))
                throw new UserFriendlyException(L("NotExistedGoods"));
            var entity = await this.GoodsItemRepository.GetAsync(id);
            return entity.MapTo<GoodsItemEntityDto>();
        }

        /// <inheritDoc/>
        public async Task<GoodsItemEntityDto> GetGoodsBySerialNo(string serialNo)
        {
            if (!await this.ExistGoods(serialNo))
                throw new UserFriendlyException(L("NotExistedGoods"));
            var entity = await this.GoodsItemRepository.FirstOrDefaultAsync(e => e.SerialNo == serialNo);
            return entity.MapTo<GoodsItemEntityDto>();
        }

        /// <inheritDoc/>
        public async Task CreateGoods(CreateGoodsInput input)
        {
            if (await this.ExistGoods(input.SerialNo))
                throw new UserFriendlyException(L("ExistedGoodsSerialNo"));
            if (await this.GoodsBrandRepository.CountAsync(e => e.Id == input.BrandId) == 0)
                throw new UserFriendlyException(L("NotExistedGoodsBrand"));
            if (await this.GoodsCategoryRepository.CountAsync(e => e.Id == input.CategoryId) == 0)
                throw new UserFriendlyException(L("NotExistedGoodsCategory"));

            var goodsItem = input.MapTo<GoodsItem>();
            goodsItem.TenantId = this.AbpSession.TenantId;
            goodsItem.GoodsList.ToList().ForEach(e => e.TenantId = this.AbpSession.TenantId);
            goodsItem = await this.GoodsItemRepository.InsertAsync(goodsItem);
        }

        /// <inheritDoc/>
        public async Task UpdateGoods(UpdateGoodsInput input)
        {
            if (!await this.ExistGoods(input.Id))
                throw new UserFriendlyException(L("NotExistedGoods"));
            if (await this.GoodsBrandRepository.CountAsync(e => e.Id == input.BrandId) == 0)
                throw new UserFriendlyException(L("NotExistedGoodsBrand"));
            if (await this.GoodsCategoryRepository.CountAsync(e => e.Id == input.CategoryId) == 0)
                throw new UserFriendlyException(L("NotExistedGoodsCategory"));

            this.GoodsItemRepository.Update(input.Id, entity =>
            {
                input.MapTo(entity);
                foreach (var goodsIn in input.GoodsList)
                {
                    var goods = entity.GoodsList.FirstOrDefault(e => e.Id == goodsIn.Id);
                    if (goods == null)
                    {
                        entity.GoodsList.Add(goodsIn.MapTo<Goods>());
                    }
                    else
                    {
                        goodsIn.MapTo(goods);
                    }
                }
            });
        }

        /// <inheritDoc/>
        public async Task EnableGoods(Guid id)
        {
            if (!await this.ExistGoods(id))
                throw new UserFriendlyException(L("NotExistedGoods"));
            var e = await this.GoodsItemRepository.GetAsync(id);
            e.IsActive = true;
        }

        /// <inheritDoc/>
        public async Task DisableGoods(Guid id)
        {
            if (!await this.ExistGoods(id))
                throw new UserFriendlyException(L("NotExistedGoods"));
            var e = await this.GoodsItemRepository.GetAsync(id);
            e.IsActive = false;
        }

        /// <inheritDoc/>
        public async Task<bool> ExistGoods(Guid id)
        {
            var count = await this.GoodsItemRepository.CountAsync(e => e.Id == id);
            return count != 0;
        }

        /// <inheritDoc/>
        public async Task<bool> ExistGoods(string serialNo)
        {
            var count = await this.GoodsItemRepository.CountAsync(e => e.SerialNo == serialNo);
            return count != 0;
        }

    }
}
